﻿using HDFSViewer.View;
using Microsoft.Practices.Prism.Commands;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using HDFSViewer.Model;
using Microsoft.Win32;
using System.IO;
using HDFSViewer.Component;
using HDFSViewer.Enum;
using HDFSViewer.Utility;

namespace HDFSViewer.ViewModel
{
    public partial class MainWindowVM
    {
        public DelegateCommand ConnectHadoop { get; set; }
        public DelegateCommand ConnectHDInsight { get; set; }
        public DelegateCommand Disconnect { get; set; }
        public DelegateCommand RefreshDirectory { get; set; }
        public DelegateCommand ClearLog { get; set; }
        public DelegateCommand ExpandAll { get; set; }
        public DelegateCommand CollapseAll { get; set; }
        public DelegateCommand AddDirectory { get; set; }
        public DelegateCommand Delete { get; set; }
        public DelegateCommand ViewFile { get; set; }
        public DelegateCommand UploadFile { get; set; }
        public DelegateCommand DownloadFile { get; set; }

        public DelegateCommand AddFile { get; set; }
        public DelegateCommand AppendFile { get; set; }

        #region Command
        private void DoConnectHadoop()
        {
            ConnectHadoopWindow win = new ConnectHadoopWindow();
            bool? result = win.ShowDialog();
            if (result.HasValue && result.Value)
            {
                var vm = win.DataContext as ConnectHadoopWindowVM;
                try
                {
                    this.SourceType = Enum.SourceType.ApacheHadoop;
                    HDFS.GetInstance().InitiateHDFS(string.Concat("http://", vm.ConnectionString), vm.UserName);
                    HDFSNodeModel model = new HDFSNodeModel(NodeType.Root, "/", "/", null);
                    this.TreeViewDataContext.Add(model);
                    this.GetDirectoryAndFile(model);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK);
                }
                finally
                {
                    this.RaiseToolBarCanExecuteChanged();
                }
            }
        }

        private bool DoCanConnectHadoop()
        {
            return !HDFS.GetInstance().Initiated;
        }

        private void DoConnectHDInsight()
        {
            ConnectHDInsightWindow win = new ConnectHDInsightWindow();
            bool? result = win.ShowDialog();
            if (result.HasValue && result.Value)
            {
                var vm = win.DataContext as ConnectHDInsightWindowVM;
                try
                {
                    this.SourceType = Enum.SourceType.AzureHDInsight;
                    HDFS.GetInstance().InitiateHDFS(vm.UserName, vm.Account, vm.Key, vm.ContainerName, vm.AutoCreateContainer);
                    HDFSNodeModel model = new HDFSNodeModel(NodeType.Root, "/", "/", null);
                    this.TreeViewDataContext.Add(model);
                    this.GetDirectoryAndFile(model);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK);
                }
                finally
                {
                    this.RaiseToolBarCanExecuteChanged();
                }
            }
        }

        private bool DoCanConnectHDInsight()
        {
            return !HDFS.GetInstance().Initiated;
        }

        private void DoDisconnect()
        {
            try
            {
                HDFS.GetInstance().Clear();
                TreeViewDataContext.Clear();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK);
            }
            finally
            {
                this.RaiseToolBarCanExecuteChanged();
            }
        }

        private bool DoCanDisconnect()
        {
            return HDFS.GetInstance().Initiated;
        }

        private void DoRefreshDirectory()
        {
            var model = this.SelectedHDFSNodeModel;
            model.Childs.Clear();
            this.GetDirectoryAndFile(model);
        }

        private bool DoCanRefreshDirectory()
        {
            return HDFS.GetInstance().Initiated && this.SelectedHDFSNodeModel != null && this.SelectedHDFSNodeModel.Type != NodeType.File;
        }

        private void DoViewFile()
        {
            this.NewFileDetailWindow(this.SelectedHDFSNodeModel);
        }

        private bool DoCanViewFile()
        {
            return this.SelectedHDFSNodeModel != null && this.SelectedHDFSNodeModel.Type == NodeType.File;
        }

        private void DoExpandAll()
        {
            this.TreeViewDataContext[0].ExpandAll();
        }

        private bool DoCanExpandAll()
        {
            return HDFS.GetInstance().Initiated;
        }

        private void DoCollapseAll()
        {
            this.TreeViewDataContext[0].CollapseAll();
        }

        private bool DoCanCollapseAll()
        {
            return HDFS.GetInstance().Initiated;
        }

        private void DoAddDirectory()
        {
            AddDirectoryWindow win = new AddDirectoryWindow(this.SelectedHDFSNodeModel.QueryPath);
            bool? result = win.ShowDialog();
            if (result.HasValue && result.Value)
            {
                var model = this.SelectedHDFSNodeModel;
                var vm = win.DataContext as AddDirectoryWindowVM;
                var remotepath = string.Concat(model.JoinPath, "/", vm.DirectoryName);
                var log = this.AddLog(LogType.CreateDirectory, LogStatus.Loading, string.Concat("remote path = ", remotepath), OperationLogModel.Result_Processing);
                if (HDFS.GetInstance().CreateHDFSDirectory(remotepath, ds =>
                {
                    try
                    {
                        if (ds.Status == TaskStatus.RanToCompletion)
                        {
                            Application.Current.Dispatcher.Invoke(() =>
                            {
                                model.Childs.Add(new HDFSNodeModel(NodeType.Directory, vm.DirectoryName, remotepath, model));
                                log.ChangeStatus(LogStatus.Success, OperationLogModel.Result_OK);
                            });
                        }
                        else
                        {
                            log.ChangeStatusWithDispatcherInvoke(LogStatus.Failed, string.Concat(ds.Status.ToString(), ":", ds.Exception != null && ds.Exception.InnerException != null ? ds.Exception.InnerException.Message : ds.Exception != null ? ds.Exception.Message : "null"));
                        }
                    }
                    catch (Exception ex)
                    {
                        log.ChangeStatusWithDispatcherInvoke(LogStatus.Failed, ex.Message);
                    }
                }) == null)
                {
                    log.ChangeStatus(LogStatus.Failed, OperationLogModel.Result_Failed);
                }
            }
        }

        private bool DoCanAddDirectory()
        {
            return this.SelectedHDFSNodeModel != null && this.SelectedHDFSNodeModel.Type != NodeType.File;
        }

        private void DoClearLog()
        {
            this.LogContext.Clear();
        }

        private bool DoCanClearLog()
        {
            return true;
        }

        private void DoDelete()
        {
            var model = this.SelectedHDFSNodeModel;
            if (MessageBox.Show(string.Concat("sure delete directory : ", model.QueryPath, " ?"), "Query", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
            {
                var log = this.AddLog(LogType.DeleteDirectory, LogStatus.Loading, model.QueryPath, OperationLogModel.Result_Processing);
                if (HDFS.GetInstance().DeleteHDFSDirectory(model.QueryPath, ds =>
                {
                    try
                    {
                        if (ds.Status == TaskStatus.RanToCompletion)
                        {
                            Application.Current.Dispatcher.Invoke(() =>
                            {
                                model.Root.Childs.Remove(model);
                                log.ChangeStatus(LogStatus.Success, OperationLogModel.Result_OK);
                            });
                        }
                        else
                        {
                            log.ChangeStatusWithDispatcherInvoke(LogStatus.Failed, string.Concat(ds.Status.ToString(), ":", ds.Exception != null && ds.Exception.InnerException != null ? ds.Exception.InnerException.Message : ds.Exception != null ? ds.Exception.Message : "null"));
                        }
                    }
                    catch (Exception ex)
                    {
                        log.ChangeStatusWithDispatcherInvoke(LogStatus.Failed, ex.Message);
                    }
                }) == null)
                {
                    log.ChangeStatus(LogStatus.Failed, OperationLogModel.Result_Failed);
                }
            }
        }

        private bool DoCanDelete()
        {
            return this.SelectedHDFSNodeModel != null && !string.IsNullOrWhiteSpace(this.SelectedHDFSNodeModel.JoinPath);
        }

        private void DoUploadFile()
        {
            OpenFileDialog ofd = new OpenFileDialog();
            bool? result = ofd.ShowDialog();
            if (result.HasValue && result.Value)
            {
                var model = this.SelectedHDFSNodeModel;
                var remotepath = string.Concat(model.JoinPath, "/", ofd.SafeFileName);
                FileInfo fi = new FileInfo(ofd.FileName);
                var log = this.AddLog(LogType.UploadFile, LogStatus.Loading, string.Concat("remote path = ", remotepath, " local path = ", ofd.FileName, " size = ", fi.Length), OperationLogModel.Result_Processing);
                if (HDFS.GetInstance().UploadHDFSFile(remotepath, ofd.FileName, ds =>
                {
                    try
                    {
                        if (ds.Status == TaskStatus.RanToCompletion)
                        {
                            Application.Current.Dispatcher.Invoke(() =>
                            {
                                model.Childs.Clear();
                                log.ChangeStatus(LogStatus.Success, OperationLogModel.Result_OK);
                            });
                            this.GetDirectoryAndFile(model);
                        }
                        else
                        {
                            log.ChangeStatusWithDispatcherInvoke(LogStatus.Failed, string.Concat(ds.Status.ToString(), ":", ds.Exception != null && ds.Exception.InnerException != null ? ds.Exception.InnerException.Message : ds.Exception != null ? ds.Exception.Message : "null"));
                        }
                    }
                    catch (Exception ex)
                    {
                        log.ChangeStatusWithDispatcherInvoke(LogStatus.Failed, ex.Message);
                    }
                }) == null)
                {
                    log.ChangeStatus(LogStatus.Failed, OperationLogModel.Result_Failed);
                }
            }
        }

        private bool DoCanUploadFile()
        {
            return this.SelectedHDFSNodeModel != null && this.SelectedHDFSNodeModel.Type != NodeType.File;
        }

        private void DoDownloadFile()
        {
            SaveFileDialog sfd = new SaveFileDialog();
            bool? result = sfd.ShowDialog();
            if (result.HasValue && result.Value)
            {
                var model = this.SelectedHDFSNodeModel;
                //var log = this.AddLog(LogType.GetFileChecksum, LogStatus.Loading, string.Concat("remote path = ", model.QueryPath), OperationLogModel.Result_Processing);
                //if (HDFS.GetInstance().GetHDFSFileChecksum(model.QueryPath, ds =>
                //{
                //try
                //{
                //if (ds.Status == TaskStatus.RanToCompletion)
                //{
                //log.ChangeStatusWithDispatcherInvoke(LogStatus.Success, OperationLogModel.Result_OK);

                var flog = this.AddLog(LogType.QueryFileStatus, LogStatus.Loading, string.Concat("remote path = ", model.QueryPath), OperationLogModel.Result_Processing);
                if (HDFS.GetInstance().GetHDFSFileStatus(model.QueryPath, fs =>
                {
                    try
                    {
                        if (fs.Status == TaskStatus.RanToCompletion)
                        {
                            flog.ChangeStatusWithDispatcherInvoke(LogStatus.Success, OperationLogModel.Result_OK);
                            var dlog = this.AddLog(LogType.DownloadFile, LogStatus.Loading, string.Concat("remote path = ", model.QueryPath, " local path = ", sfd.FileName), OperationLogModel.Result_Processing);
                            HDFS.GetInstance().DownloadHDFSFile(model.QueryPath, File.OpenWrite(sfd.FileName), 0, DownloadSegmentSize, (int)fs.Result.Length, dlog);
                        }
                        else
                        {
                            flog.ChangeStatusWithDispatcherInvoke(LogStatus.Failed, fs.Status.ToString());
                        }
                    }
                    catch (Exception fex)
                    {
                        flog.ChangeStatusWithDispatcherInvoke(LogStatus.Failed, fex.Message);
                    }
                }) == null)
                {
                    flog.ChangeStatus(LogStatus.Failed, OperationLogModel.Result_Failed);
                }

                //}
                //else
                //{
                //    log.ChangeStatusWithDispatcherInvoke(LogStatus.Failed, string.Concat(ds.Status.ToString(), ":", ds.Exception != null && ds.Exception.InnerException != null ? ds.Exception.InnerException.Message : ds.Exception != null ? ds.Exception.Message : "null"));
                //}
                //}
                //catch (Exception ex)
                //{
                //    log.ChangeStatusWithDispatcherInvoke(LogStatus.Failed, ex.Message);
                //}
                //}) == null)
                //{
                //    log.ChangeStatus(LogStatus.Failed, OperationLogModel.Result_Failed);
                //}
            }
        }

        private bool DoCanDownloadFile()
        {
            return this.SelectedHDFSNodeModel != null && this.SelectedHDFSNodeModel.Type == NodeType.File;
        }

        private void DoAddFile()
        {
            var model = this.SelectedHDFSNodeModel;
            AddFileWindow win = new AddFileWindow(model.QueryPath, null, false);
            bool? result = win.ShowDialog();
            if (result.HasValue && result.Value)
            {
                var vm = win.DataContext as AddFileWindowVM;
                string remotepath = string.Concat(vm.DirectoryPath, "/", vm.Name);
                var log = this.AddLog(LogType.AddFile, LogStatus.Loading, string.Concat("remote path = ", remotepath), OperationLogModel.Result_Processing);
                MemoryStream stream = null;
                if (vm.SelectedEncoding == "HEX") stream = ByteUtility.GetBytesFromHexString(vm.Content);
                else
                {
                    stream = new MemoryStream(EncodingUtility.ConvertEncoding(vm.SelectedEncoding).GetBytes(vm.Content));
                }

                if (stream != null)
                {
                    if (HDFS.GetInstance().AddHDFSFile(remotepath, stream, ds =>
                    {
                        try
                        {
                            if (ds.Status == TaskStatus.RanToCompletion)
                            {
                                Application.Current.Dispatcher.Invoke(() =>
                                {
                                    model.Childs.Clear();
                                    log.ChangeStatus(LogStatus.Success, OperationLogModel.Result_OK);
                                });
                                this.GetDirectoryAndFile(model);
                            }
                            else
                            {
                                log.ChangeStatusWithDispatcherInvoke(LogStatus.Failed, string.Concat(ds.Status.ToString(), ":", ds.Exception != null && ds.Exception.InnerException != null ? ds.Exception.InnerException.Message : ds.Exception != null ? ds.Exception.Message : "null"));
                            }
                        }
                        catch (Exception ex)
                        {
                            log.ChangeStatusWithDispatcherInvoke(LogStatus.Failed, ex.Message);
                        }
                    }) == null)
                    {
                        log.ChangeStatus(LogStatus.Failed, OperationLogModel.Result_Failed);
                    }
                }
                else
                {
                    log.ChangeStatus(LogStatus.Failed, "illegal content");
                }
            }
        }

        private bool DoCanAddFile()
        {
            return this.SelectedHDFSNodeModel != null && this.SelectedHDFSNodeModel.Type != NodeType.File;
        }

        private void DoAppendFile()
        {
            var model = this.SelectedHDFSNodeModel;
            AddFileWindow win = new AddFileWindow(model.Root.QueryPath, model.Name, true);
            bool? result = win.ShowDialog();
            if (result.HasValue && result.Value)
            {
                var vm = win.DataContext as AddFileWindowVM;
                string remotepath = string.Concat(vm.DirectoryPath, "/", vm.Name);
                var log = this.AddLog(LogType.AppendFile, LogStatus.Loading, string.Concat("remote path = ", remotepath), OperationLogModel.Result_Processing);
                MemoryStream stream = null;
                if (vm.SelectedEncoding == "HEX") stream = ByteUtility.GetBytesFromHexString(vm.Content);
                else
                {
                    stream = new MemoryStream(EncodingUtility.ConvertEncoding(vm.SelectedEncoding).GetBytes(vm.Content));
                }

                if (stream != null)
                {
                    stream.Position = 0;
                    if (HDFS.GetInstance().AppendHDFSFile(remotepath, stream, ds =>
                    {
                        try
                        {
                            if (ds.Status == TaskStatus.RanToCompletion)
                            {
                                log.ChangeStatusWithDispatcherInvoke(LogStatus.Success, OperationLogModel.Result_OK);
                            }
                            else
                            {
                                log.ChangeStatusWithDispatcherInvoke(LogStatus.Failed, string.Concat(ds.Status.ToString(), ":", ds.Exception != null && ds.Exception.InnerException != null ? ds.Exception.InnerException.Message : ds.Exception != null ? ds.Exception.Message : "null"));
                            }
                        }
                        catch (Exception ex)
                        {
                            log.ChangeStatusWithDispatcherInvoke(LogStatus.Failed, ex.Message);
                        }
                    }) == null)
                    {
                        log.ChangeStatus(LogStatus.Failed, OperationLogModel.Result_Failed);
                    }
                }
                else
                {
                    log.ChangeStatus(LogStatus.Failed, "illegal content");
                }
            }
        }

        private bool DoCanAppendFile()
        {
            return this.SelectedHDFSNodeModel != null && this.SelectedHDFSNodeModel.Type == NodeType.File;
        }

        #endregion

        private void GetDirectoryAndFile(HDFSNodeModel model)
        {
            var log = this.AddLog(LogType.QueryDirectory, LogStatus.Loading, string.Concat("path = ", model.QueryPath), OperationLogModel.Result_Processing);
            string path = null;
            switch (this.SourceType)
            {
                case Enum.SourceType.ApacheHadoop:
                    path = model.QueryPath;
                    break;
                case Enum.SourceType.AzureHDInsight:
                    path = model.QueryPath.EndsWith("/") ? model.QueryPath : string.Concat(model.QueryPath, "/");
                    break;
            }

            if (HDFS.GetInstance().GetHDFSDirectoryStatus(path, ds =>
            {
                try
                {
                    if (ds.Status == TaskStatus.RanToCompletion)
                    {
                        Application.Current.Dispatcher.Invoke(() =>
                        {
                            model.IsLoaded = true;

                            switch (this.SourceType)
                            {
                                case Enum.SourceType.ApacheHadoop:
                                    foreach (var sub in ds.Result.Directories)
                                    {
                                        var dirnode = new HDFSNodeModel(NodeType.Directory, sub.PathSuffix, string.Concat(model.JoinPath, "/", sub.PathSuffix), model);
                                        model.Childs.Add(dirnode);
                                    }
                                    foreach (var sub in ds.Result.Files)
                                    {
                                        var filenode = new HDFSNodeModel(NodeType.File, sub.PathSuffix, string.Concat(model.JoinPath, "/", sub.PathSuffix), model);
                                        model.Childs.Add(filenode);
                                    }
                                    break;
                                case Enum.SourceType.AzureHDInsight:
                                    foreach (var sub in ds.Result.Directories)
                                    {
                                        if (sub.PathSuffix.Contains('/'))
                                        {
                                            var name = sub.PathSuffix.Substring(sub.PathSuffix.LastIndexOf("/") + 1);
                                            var dirnode = new HDFSNodeModel(NodeType.Directory, name, string.Concat(model.JoinPath, "/", name), model);
                                            model.Childs.Add(dirnode);
                                        }
                                        else
                                        {
                                            var dirnode = new HDFSNodeModel(NodeType.Directory, sub.PathSuffix, string.Concat(model.JoinPath, "/", sub.PathSuffix), model);
                                            model.Childs.Add(dirnode);
                                        }
                                    }
                                    foreach (var sub in ds.Result.Files)
                                    {
                                        if (sub.PathSuffix.Contains('/'))
                                        {
                                            var name = sub.PathSuffix.Substring(sub.PathSuffix.LastIndexOf("/") + 1);
                                            var filenode = new HDFSNodeModel(NodeType.File, name, string.Concat(model.JoinPath, "/", name), model);
                                            model.Childs.Add(filenode);
                                        }
                                        else
                                        {
                                            var filenode = new HDFSNodeModel(NodeType.File, sub.PathSuffix, string.Concat(model.JoinPath, "/", sub.PathSuffix), model);
                                            model.Childs.Add(filenode);
                                        }
                                    }
                                    break;
                            }
                            log.ChangeStatus(LogStatus.Success, OperationLogModel.Result_OK);
                        });
                    }
                    else
                    {
                        log.ChangeStatusWithDispatcherInvoke(LogStatus.Failed, string.Concat(ds.Status.ToString(), ":", ds.Exception != null && ds.Exception.InnerException != null ? ds.Exception.InnerException.Message : ds.Exception != null ? ds.Exception.Message : "null"));
                    }
                }
                catch (Exception ex)
                {
                    log.ChangeStatusWithDispatcherInvoke(LogStatus.Failed, ex.Message);
                }

            }) == null)
            {
                log.ChangeStatus(LogStatus.Failed, OperationLogModel.Result_Failed);
            }
        }
    }
}